﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Server
{
    class UserSession
    {
        public IPEndPoint m_RemoteIP;
        MessageDispatcher m_Dispatcher = new MessageDispatcher();
        Queue<byte[]> m_MessageBytes = new Queue<byte[]>();
        public bool IsConnect { private set; get; }
        bool m_IsStartDispose;
        public uint m_UserID { private set; get; }
        float m_LastPingTime;
        public UnityEngine.Vector3 m_Pos;

        public UserSession(IPEndPoint ep, uint userId)
        {
            m_LastPingTime = MessageManager.GetCurElapsedTime();
            m_RemoteIP = ep;
            m_UserID = userId;
            m_Dispatcher.Register<HelloMessage>(EMessageType.HELLO, (msg) =>
            {
                ServerApp.Send(EMessageType.HELLO, new HelloMessage() { m_UserID = m_UserID }, m_UserID);
            });
            m_Dispatcher.Register<BaseMessage>(EMessageType.ACK, (msg) =>
            {
                IsConnect = true;
            });
            m_Dispatcher.Register<C2S_JoinMessage>(EMessageType.JOIN, (msg) =>
            {
                var v = ServerApp.GetRandomPos();
                ServerApp.IterateUsers((user) =>
                {
                    if (user.m_UserID == m_UserID)
                    {
                        return;
                    }
                    ServerApp.Send(EMessageType.JOIN, new S2C_JoinMessage() { m_UserID = user.m_UserID, m_Pos = user.m_Pos }, m_UserID);
                });
                ServerApp.Send(EMessageType.JOIN, new S2C_JoinMessage() { m_UserID = m_UserID,
                    //m_Pos = v
                });
            });
            m_Dispatcher.Register<C2S_SyncMessage>(EMessageType.SYNC, (msg) =>
            {
                m_Pos = msg.m_Pos;
                LogManager.Log("sync set pos " + m_Pos + ", id=" + m_UserID); 
            });
            m_Dispatcher.Register<BaseMessage>(EMessageType.PING, OnPing);
            LogManager.Log("create new user " + m_UserID);

            // process receive message
            Task.Run(() =>
            {
                while (true)
                {
                    if (m_IsStartDispose)
                    {
                        return;
                    }

                    if (!IsConnect)
                    {
                        //continue;
                    }
                    if (m_MessageBytes.Count > 0)
                    {
                        ProcessMessage(m_MessageBytes.Dequeue());
                    }
                    Thread.Sleep(1);
                }
            });
        }

        ~UserSession()
        {
            //Stop();
            LogManager.Log("~UserSession()");
        }

        void OnPing(BaseMessage msg)
        {
            m_LastPingTime = MessageManager.GetCurElapsedTime();
        }

        public void Tick()
        {
            if (MessageManager.GetCurElapsedTime() - m_LastPingTime > 5)
            {
                //ServerApp.StopUser(this);
                //return;
            }

            if (!IsConnect)
            {
                return;
            }

            if (m_UserID % ServerApp.m_PosSyncInterval == ServerApp.TickCount % ServerApp.m_PosSyncInterval)
            {
                var ms = new S2C_SyncMessage();
                var dis = 0f;
                ServerApp.IterateUsers((user) =>
                {
                    dis = UnityEngine.Vector3.Distance(m_Pos, user.m_Pos);
                    if (dis > 10)
                    {
                        //return;
                    }

                    //if ((dis > 5 && ServerApp.TickCount % 3 == 0)
                    //    || (dis > 3 && ServerApp.TickCount % 2 == 0)
                    //    || dis <= 3)
                    {
                        LogManager.Log("send pos=" + user.m_Pos + ", target=" + user.m_UserID + ", id=" + m_UserID);
                        ms.m_Players.Add(new S2C_SyncPlayerMessage { m_UserID = user.m_UserID, m_Pos = user.m_Pos });
                    }
                });
                ServerApp.Send(EMessageType.SYNC, ms);
            }
        }

        public void Stop()
        {
            IsConnect = false;
            LogManager.Log("stop user " + m_UserID);
            m_IsStartDispose = true;
            m_Dispatcher = null;
        }

        public void ReceiveMessage(byte[] bs)
        {
            if (MessageManager.GetMessageType(bs) == EMessageType.SYNC)
            {
                var m = MessageManager.Unpack(bs) as C2S_SyncMessage;
                string s = string.Empty;
                s += "pos="  + m.m_Pos + ", id=" + m.m_UserID;
                LogManager.Log("receive SYNC " + s);
            }
            else
            {
                LogManager.Log("receive " + MessageManager.GetMessageType(bs));
            }
            m_MessageBytes.Enqueue(bs);
        }

        void ProcessMessage(byte[] bs)
        {
            if (!ServerApp.HideLog(MessageManager.GetMessageType(bs)))
            {
                LogManager.Log("process " + MessageManager.GetMessageType(bs) + ", id=" + m_UserID);
            }
            m_Dispatcher.Invoke(MessageManager.GetMessageType(bs), MessageManager.Unpack(bs));
            //ServerApp.PutBuff(bs);
        }
    }
}
